/*
 * IndiceDatos.cpp
 *
 *  Created on: May 20, 2012
 *      Author: lucasj
 */

#include "IndiceDatos.h"

DataIndex::IndiceDatos::IndiceDatos(Validador& validator):dimension(validator), columna(validator) {

}

void DataIndex::IndiceDatos::SetElem(std::string &linea, int cod) {
		this->map[cod] = linea;
		this->dimension.Cargar(linea, cod);
		this->columna.Cargar(linea);
}

DataIndex::IndiceDatos::~IndiceDatos() {

}

void DataIndex::IndiceDatos::obtenerSiguientePosicion(unsigned int
					    &posAnterior, unsigned int &posActual, std::string &linea) {
	  	  posAnterior = posActual;
	  	  posActual = (linea.find("|", posAnterior + 1));
}

// Funcionamiento LLenarResultado:
// Recibe las dimensiones de la consulta a buscar en valor.
// lo parsea y lo busca en el índice de dimensiones. Cant Valores
// es la cantidad de valores de la consulta que se va contando
// a medida que se parsea la línea.

bool DataIndex::IndiceDatos::LlenarResultado(Consulta& pregunta, ResultIndex::IndiceResultado& resultIndex, int &cantValores, Consulta& hechos) {

	cantValores = 0;
	std::list<Atributo_consulta*> lista = pregunta.getAtributos();
	std::list<Atributo_consulta*>::iterator i;
	i = lista.begin();
	bool encontrado = true;
	while(i != lista.end() && (encontrado == true)) {
		if ((*i)->getTipo() == D) {
			++cantValores;
			std::string dimensionBuscar = (*i)->getNombre();
			if (dimensionBuscar == "TODO") {
				std::list<std::string> listaColumna;
				this->columna.obtenerListado((*i)->getPos(), listaColumna);
				recorrerYBuscar(listaColumna, resultIndex, (*i)->getPos());
			} else encontrado = this->dimension.Buscar(dimensionBuscar, resultIndex, (*i)->getPos());
		} else {
			Atributo_consulta* hecho = new Atributo_consulta((*i)->getNombre(), (*i)->getTipo(), (*i)->getPos(), (*i)->getAgregacion());
			hechos.insertarAtributo(hecho);
		}
		++i;
	}
	return encontrado;
}

void DataIndex::IndiceDatos::recorrerYBuscar(std::list<std::string>& lista, ResultIndex::IndiceResultado& resultIndex, int pos) {
	std::list<std::string>::iterator i;
	for(i = lista.begin(); i != lista.end(); ++i) {
		this->dimension.Buscar(*i, resultIndex, pos);
	}
}


/*void DataIndex::IndiceDatos::ImprimirResultados(ResultIndex::IndiceResultado& coincidencias, std::ostringstream &res) {
	iteradorResultado i;
	iteratorDatos j;
	for (i = coincidencias.begin(); i != coincidencias.end(); ++i) {
		j = this->map.begin();
		while (j != this->map.end() && (i->first != j->first))
			++j;
		if (i->first == j->first) {
			// res << j->first << " ";
			res << j->second << std::endl;
		}
	}
}
*/

int ConvertirAInt(const std::string &dimension) {
	std::stringstream convert(dimension);
	int res = 890;
	convert >> res;
	return res;
}

int DataIndex::IndiceDatos::obtenerValor(int posIndice, int posHecho) {
	std::string dato = this->map[posIndice];
	unsigned int posActual = dato.find("|");
	unsigned int posAnterior = 0;
	int i = 0;
	while(i < posHecho) {
		obtenerSiguientePosicion(posAnterior, posActual, dato);
		++i;
	}
	std::string valorString = dato.substr(posAnterior + 1, posActual - posAnterior - 1);
	return ConvertirAInt(valorString);
}

void DataIndex::IndiceDatos::Sumar(int& valor, int& otroValor) {
	valor = valor + otroValor;
}

void DataIndex::IndiceDatos::Max(int& valor, int& otroValor) {
	if (otroValor > valor)
		valor = otroValor;
}

 void DataIndex::IndiceDatos::Min(int& valor, int& otroValor) {
	if (otroValor < valor)
		valor = otroValor;
}

 void DataIndex::IndiceDatos::Count(int& valor) {
	 ++valor;
}

int DataIndex::IndiceDatos::obtenerPosicionDimension(int posIndice, std::string& valorActual) {
	std::string dato = this->map[posIndice];
	unsigned int posActual = dato.find("|");
	unsigned int posAnterior = 0;
	int i = 0;
	std::string valorString = dato.substr(posAnterior, posActual);
	valorString.append("|");
	while(valorString != valorActual) {
		obtenerSiguientePosicion(posAnterior, posActual, dato);
		valorString = dato.substr(posAnterior + 1, posActual - posAnterior - 1);
		valorString.append("|");
		++i;
	}

	return i;
}

void DataIndex::IndiceDatos::AgregarCoincidencias(ResultIndex::IndiceResultado& coincidencias, AggregationIndex::IndiceAgregacion& res, unsigned int posHecho, char agregacion) {
	iteradorResultado2 inicio;
	iteradorResultado2 fin;
	iteradorResultado2 i;
	coincidencias.VincularIteradores(inicio, fin);
	std::string valorActual = inicio->first;
	int valor = INICIO;
	int count;
	i = inicio;
	while (i != fin) {
		if (i->first == valorActual) {
			int otroValor = obtenerValor(i->second, posHecho);
			switch(agregacion) {
			case SUMA: {
				if (valor == INICIO)
					valor = 0;
				Sumar(valor, otroValor);
				break;
			}
			case MAXI: {
				if (valor == INICIO)
					valor = obtenerValor(inicio->second, posHecho);
				Max(valor, otroValor);
				break;
			}
			case MIN: {
				if (valor == INICIO)
					valor = obtenerValor(inicio->second, posHecho);
				Min(valor, otroValor);
				break;
			}
			case COUNT: {
				if (valor == INICIO)
					valor = 0;
				Count(valor);
				break;
			}
			case AVG: {
				if (valor == INICIO) {
					valor = 0;
					count = 0;
				}
				Count(count);
				Sumar(valor, otroValor);
				break;
			}
			default: break;
			}
			++i;
		} else {
			if (agregacion == AVG) {
				valor = valor/count;
			}
			res.SetElem(valorActual, valor, posHecho);
			valorActual =i->first;
			valor = INICIO;
		}
	}
	if (agregacion == AVG) {
		valor = valor/count;
	}
	res.SetElem(valorActual, valor, posHecho);
}

bool VerificarNone(std::list<Atributo_consulta*> lista) {
	std::list<Atributo_consulta*>::iterator i;
	i = lista.begin();
	while(i != lista.end() && (*i)->getAgregacion() != NONE) {
		++i;
	}
	if (i == lista.end()) {
		return false;
	} else return true;
}

void DataIndex::IndiceDatos::AgregarSinNone(
		std::list<Atributo_consulta*>& lista,
		ResultIndex::IndiceResultado& coincidencias,
		AggregationIndex::IndiceAgregacion& resAgg) {
	std::list<Atributo_consulta*>::iterator i;
	for (i = lista.begin(); i != lista.end(); ++i) {
		char agregacion = (*i)->getAgregacion();
		AgregarCoincidencias(coincidencias, resAgg, (*i)->getPos(), agregacion);
	}
}

void DataIndex::IndiceDatos::ImprimirCoincidencias(std::list<Atributo_consulta*>& lista, ResultIndex::IndiceResultado& coincidencias, std::ostringstream& res) {
	iteradorResultado2 inicio;
	iteradorResultado2 fin;
	iteradorResultado2 iResult;
	coincidencias.VincularIteradores(inicio, fin);
	std::list<Atributo_consulta*>::iterator iHechos;
	for (iResult = inicio; iResult != fin; ++iResult) {
		res << iResult->first;
		for (iHechos = lista.begin(); iHechos != lista.end(); ++iHechos) {
			int Valor = obtenerValor(iResult->second, (*iHechos)->getPos());
			res << Valor << '|';
		}
		res << std::endl;
	}
}


void DataIndex::IndiceDatos::Agregar(ResultIndex::IndiceResultado& coincidencias, Consulta& hechos, std::ostringstream& res) {
	std::list<Atributo_consulta*> lista = hechos.getAtributos();

	AggregationIndex::IndiceAgregacion resAgg;
	if (VerificarNone(lista) == false) {
		AgregarSinNone(lista, coincidencias, resAgg);
		res << resAgg;
	} else {
		ImprimirCoincidencias(lista, coincidencias, res);
	}

}

void DataIndex::IndiceDatos::Consultar(Consulta& pregunta, std::ostringstream &res) {
	int cantValores = 0;
	ResultIndex::IndiceResultado resultIndex;
	Consulta hechos;
	if (LlenarResultado(pregunta, resultIndex, cantValores, hechos) == true) {
		//res << resultIndex;
		ResultIndex::IndiceResultado coincidencias;
		resultIndex.ObtenerDatos(cantValores, coincidencias);
		coincidencias.Swap();
		//res << coincidencias;
		Agregar(coincidencias, hechos, res);
	} else res << "";
}

std::ostringstream& DataIndex::operator<<(std::ostringstream& oss, DataIndex::IndiceDatos &indice) {
		iteratorDatos it;
		oss << "Indice Datos:" << std::endl;
		for (it = indice.map.begin(); it != indice.map.end(); ++it) {
			oss << it->first << " ";
			oss << it->second << std::endl;
		}
		oss << " " << std::endl;
		oss << "Indice Dimensiones:" << std::endl;
		oss << indice.dimension;
		oss << "Indice Columnas:" << std::endl;
		oss << indice.columna;
		return oss;
}

void DataIndex::IndiceDatos::obtenerValorColumna(Consulta& pregunta, std::ostringstream &res) {
	this->columna.obtenerValorColumna(pregunta, res);
}

